package com.sec.android.app.myfiles.module.abstraction;

import android.app.FragmentManager;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;

import com.sec.android.app.myfiles.feature.PrivateModeMgr;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.info.MediaFile;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.operation.FileOperationArgs;
import com.sec.android.app.myfiles.operation.FileOperationException;
import com.sec.android.app.myfiles.operation.FileOperator;
import com.sec.android.app.myfiles.operation.FileOperator.Operation;
import com.sec.android.app.myfiles.operation.ProgressListener;
import com.sec.android.app.myfiles.util.FileUtils;
import com.sec.android.app.myfiles.util.StorageMonitor;
import com.sec.android.app.myfiles.util.chain.Chain;

import java.util.ArrayList;

public abstract class AbsFileOperationImp implements Chain<AbsFileOperationImp> {
    protected Context mContext;
    protected ProgressListener mProgresslistener;

    protected AbsFileOperationImp mNext = null;
    protected boolean mCancel = false;

    public AbsFileOperationImp(Context context, ProgressListener listener) {
        mContext = context;
        mCancel = false;
        mProgresslistener = listener;
    }

    @Override
    public void setNext(AbsFileOperationImp next) {
        mNext = next;
    }

    public final ArrayList<FileRecord> getListInDirectory(FileRecord record) {
        ArrayList<FileRecord> ret = null;
        if (isSupport(record)) {
            ret = _getListInDirectory(record);
        } else {
            if (mNext != null) {
                ret = mNext.getListInDirectory(record);
            }
        }

        return ret;
    }

    public final void cancel() {
        mCancel = true;
        _cancel();

        if (mNext != null) {
            mNext.cancel();
        }
    }

    /**
     * Copy interface method
     *
     * @param src Source file record
     * @param dst destination file record
     * @return true/false
     */
    public final boolean copy(FileRecord src, FileRecord dst) throws FileOperationException {
        boolean bRet = false;

        if (src.equals(dst)) {
            return true;
        }

        if (isSupportCopyMove(src, dst)) {
            bRet = _copy(src, dst);
        } else {
            if (mNext != null) {
                bRet = mNext.copy(src, dst);
            }
        }

        return bRet;
    }

    public final boolean copy(ArrayList<FileRecord> src, FileRecord dst, FragmentManager fragmentManager, boolean singleFile, boolean dupCheck,
                              FileOperator operator) throws FileOperationException {
        boolean bRet = false;
        FileRecord start = src.get(0);

        if (isSupportCopyMove(start, dst) && !isSupportRecursiveCopyMove()) {
            bRet = _copy(src, dst, fragmentManager, singleFile, dupCheck, operator);
        } else {
            if (mNext != null) {
                bRet = mNext.copy(src, dst, fragmentManager, singleFile, dupCheck, operator);
            }
        }

        return bRet;
    }

    /**
     * Move interface method
     *
     * @param src Source file record
     * @param dst destination file record
     * @return true/false
     */
    public final boolean move(FileRecord src, FileRecord dst) throws FileOperationException {
        boolean bRet = false;

        if (src.equals(dst)) {
            return true;
        }

        if (isSupportCopyMove(src, dst)) {
            bRet = _move(src, dst);
        } else {
            if (mNext != null) {
                bRet = mNext.move(src, dst);
            }
        }

        return bRet;
    }

    public final boolean move(ArrayList<FileRecord> src, FileRecord dst, FragmentManager fragmentManager, boolean singleFile,
                              FileOperator operator, ArrayList<FileRecord> handledSrcList) throws FileOperationException {
        boolean bRet = false;
        FileRecord start = src.get(0);

        if (isSupportCopyMove(start, dst) && !isSupportRecursiveCopyMove()) {
            bRet = _move(src, dst, fragmentManager, singleFile, operator, handledSrcList);
        } else {
            if (mNext != null) {
                bRet = mNext.move(src, dst, fragmentManager, singleFile, operator, handledSrcList);
            }
        }

        return bRet;
    }

    /**
     * delete interface method
     *
     * @param record file record
     * @return true/false
     */
    public final boolean delete(FileRecord record) throws FileOperationException {
        boolean bRet = false;

        if (isSupport(record)) {
            bRet = _delete(record);
        } else {
            if (mNext != null) {
                bRet = mNext.delete(record);
            }
        }

        return bRet;
    }

    public final boolean delete(ArrayList<FileRecord> recordList, FileOperator operator) throws FileOperationException {
        boolean bRet = false;

        if (isSupport(recordList.get(0))) {
            bRet = _delete(recordList, operator);
        } else {
            if (!bRet && mNext != null) {
                bRet = mNext.delete(recordList, operator);
            }
        }

        return bRet;
    }

    public final boolean rename(FileRecord src, FileRecord dst) throws FileOperationException {
        boolean bRet = false;

        if (src.equals(dst)) {
            return true;
        }

        if (isSupportCopyMove(src, dst)) {
            bRet = _rename(src, dst);
        } else {
            if (mNext != null) {
                bRet = mNext.rename(src, dst);
            }
        }

        return bRet;
    }

    public FileRecord mkdir(FileRecord dst, String name) throws FileOperationException {
        FileRecord ret = null;

        if (isSupport(dst)) {
            ret = _mkdir(dst, name);
        } else {
            if (mNext != null) {
                ret = mNext.mkdir(dst, name);
            }
        }

        return ret;
    }

    public final FileRecord getUniqueFileRecord(FileRecord record) {
        FileRecord ret = null;

        if (isSupport(record)) {
            ret = _getUniqueFileRecord(record);
        } else {
            if (mNext != null) {
                ret = mNext.getUniqueFileRecord(record);
            }
        }

        return ret;
    }

    public void preOperation(Operation op, FileRecord src, FileRecord dst) {
        boolean bSupport = false;
        if (src != null && dst != null) {
            bSupport = isSupportCopyMove(src, dst);
        } else if (src != null) {
            bSupport = isSupport(src);
        } else if (dst != null) {
            bSupport = isSupport(dst);
        }

        if (bSupport) {
            _preOperation(op);
        } else {
            if (mNext != null) {
                mNext.preOperation(op, src, dst);
            }
        }
    }

    public void postOperation(Operation op, FileOperationArgs args, int handledCount) {
        FileRecord src;
        if (args.mSrcList != null && !args.mSrcList.isEmpty()) {
            src = args.mSrcList.get(0);
        } else {
            src = args.mSrc;
        }
        FileRecord dst = args.mDst;
        boolean bSupport = false;
        if (src != null && dst != null) {
            bSupport = isSupportCopyMove(src, dst);
        } else if (src != null) {
            bSupport = isSupport(src);
        } else if (dst != null) {
            bSupport = isSupport(dst);
        }

        if (bSupport) {
            _postOperation(op, args, handledCount);
        } else {
            if (mNext != null) {
                mNext.postOperation(op, args, handledCount);
            }
        }
    }

    protected void scanForOperation(Operation op, FileOperationArgs args, int handledCount) {
        final ArrayList<String> pathList = new ArrayList<>();
        final ArrayList<String> fileList = new ArrayList<>();

        makeScanLists(op, pathList, fileList, args, handledCount);

        if (!pathList.isEmpty()) {
            FileUtils.sendScan(mContext, pathList.toArray(new String[]{}), new MediaScannerConnection.OnScanCompletedListener() {
                @Override
                public void onScanCompleted(String s, Uri uri) {
                    Log.d(this, "[mediascanner] scan directory done - " + s + "  " + uri);
                }
            });
        }

        if (!fileList.isEmpty()) {
            ArrayList<String> mimeTypeList = new ArrayList<>();
            for (String file : fileList) {
                mimeTypeList.add(MediaFile.getMimeType(file));
            }
            MediaScannerConnection.scanFile(mContext, fileList.toArray(new String[]{}), mimeTypeList.toArray(new String[]{}), new MediaScannerConnection.OnScanCompletedListener() {
                @Override
                public void onScanCompleted(String s, Uri uri) {
                    Log.i(this, "[mediascanner] scan file done - " + s + "  " + uri);
                }
            });
        }
    }

    protected void addRootPath(ArrayList<String> pathList, ArrayList<FileRecord> srcList, FileRecord dst) {
        if (srcList != null) {
            if (hasSdCardPath(srcList, dst)) {
                pathList.add(StorageMonitor.getExtSDCardPath());
            }
            if (hasDevicePath(srcList, dst)) {
                pathList.add(AppConstants.StoragePath.INTERNAL_ROOT);
            }
            if (hasPrivatePath(srcList, dst)) {
                pathList.add(PrivateModeMgr.getInstance(mContext).getRootDir());
            }
        }
    }

    private boolean hasDevicePath(ArrayList<FileRecord> srcList, FileRecord dst) {
        boolean ret = false;
        if (dst != null && dst.getFullPath().startsWith(AppConstants.StoragePath.INTERNAL_ROOT)) {
            ret = true;
        } else {
            for (FileRecord src : srcList) {
                if (src != null && src.getFullPath().startsWith(AppConstants.StoragePath.INTERNAL_ROOT)) {
                    ret = true;
                    break;
                }
            }
        }
        return ret;
    }

    private boolean hasSdCardPath(ArrayList<FileRecord> srcList, FileRecord dst) {
        boolean ret = false;
        if (StorageMonitor.isSdCardMounted()) {
            if (dst != null && StorageMonitor.isMountedSdCardPath(dst.getFullPath())) {
                ret = true;
            } else {
                for (FileRecord src : srcList) {
                    if (src != null && StorageMonitor.isMountedSdCardPath(src.getFullPath())) {
                        ret = true;
                        break;
                    }
                }
            }
        }
        return ret;
    }

    private boolean hasPrivatePath(ArrayList<FileRecord> srcList, FileRecord dst) {
        boolean ret = false;
        PrivateModeMgr privateModeMgr = PrivateModeMgr.getInstance(mContext);

        if (privateModeMgr.isPrivateDirMounted()) {
            String mPrivateRoot = privateModeMgr.getRootDir();
            if (dst != null && dst.getFullPath().startsWith(mPrivateRoot)) {
                ret = true;
            } else {
                for (FileRecord src : srcList) {
                    if (src != null && src.getFullPath().startsWith(mPrivateRoot)) {
                        ret = true;
                        break;
                    }
                }
            }
        }
        return ret;
    }

    protected abstract void makeScanLists(Operation op, ArrayList<String> pathList, ArrayList<String> fileList, FileOperationArgs args, int handledCount);

    protected abstract boolean isSupport(FileRecord record);

    protected abstract boolean isSupportCopyMove(FileRecord src, FileRecord dst);

    protected abstract boolean isSupportRecursiveCopyMove();

    /**
     * Copy implement method
     *
     * @param src Source file record
     * @param dst destination file record
     * @return true/false
     * @throws FileOperationException
     */
    protected abstract boolean _copy(FileRecord src, FileRecord dst) throws FileOperationException;

    protected abstract boolean _copy(ArrayList<FileRecord> src, FileRecord dst, FragmentManager fragmentManager, boolean singleFile,
                                     boolean dupCheck, FileOperator operator) throws FileOperationException;

    /**
     * Move implement method
     *
     * @param src Source file record
     * @param dst destination file record
     * @return true/false
     * @throws FileOperationException
     */
    protected abstract boolean _move(FileRecord src, FileRecord dst) throws FileOperationException;

    protected abstract boolean _move(ArrayList<FileRecord> src, FileRecord dst, FragmentManager fragmentManager, boolean singleFile,
                                     FileOperator operator, ArrayList<FileRecord> handledSrcList) throws FileOperationException;

    /**
     * Rename implement method
     *
     * @param src Source file record
     * @param dst destination file record
     * @return true/false
     * @throws FileOperationException
     */
    protected abstract boolean _rename(FileRecord src, FileRecord dst) throws FileOperationException;

    /**
     * delete implement method
     *
     * @param record file record
     * @return true/false
     * @throws FileOperationException
     */
    protected abstract boolean _delete(FileRecord record) throws FileOperationException;

    /**
     * delete implement method
     *
     * @param recordList list of file record
     * @return true/false
     * @throws FileOperationException
     */
    protected boolean _delete(ArrayList<FileRecord> recordList, FileOperator operator) throws FileOperationException {
        return false;
    }

    /**
     * Create directory implement method
     *
     * @param dst  path record
     * @param name folder name
     * @return created folder record
     */
    protected abstract FileRecord _mkdir(FileRecord dst, String name) throws FileOperationException;

    /**
     * get Unique FileRecord for duplicated file name
     *
     * @param record duplicated file record
     * @return Unique FileRecord
     */
    protected abstract FileRecord _getUniqueFileRecord(FileRecord record);

    protected abstract ArrayList<FileRecord> _getListInDirectory(FileRecord record);

    protected abstract void _preOperation(Operation operationType);

    protected abstract void _postOperation(Operation operationType, FileOperationArgs args, int handledCount);

    protected void _cancel() {
    }
}
